import std;

var RED : Color = (224, 0, 0);
var DARK_BLUE : Color = (0, 0, 128);
var WHITE : Color = (255, 255, 255);
var BLACK : Color = (0, 0, 0);

var WIDTH = 800;
var HEIGHT = 800;

var PADDING = 40;
var GRID_WIDTH = (WIDTH - PADDING * 2) / 8;
var GRID_HEIGHT = (HEIGHT - PADDING * 2) / 8;
var RADIUS = 40;

var s = HiScreen(WIDTH, HEIGHT);

record Game {
    board : List[List[Int]];
    turn : Int;
}

var game : Game = Game([
    [0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 2, 0, 0, 0],
    [0, 0, 0, 2, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0],
], 1);

def draw_lines(x : Int, y : Int, length : Int, dx : Int, dy : Int, n : Int, color : Color, s : HiScreen) {
    if (n == 0) { return s; }

    if (dx == 0) {
        drawLine(x, y, x + length, y, color, s);
        return draw_lines(x, y + dy, length, dx, dy, n - 1, color, s);
    }
    
    if (dy == 0) {
        drawLine(x, y, x, y + length, color, s);
        return draw_lines(x + dx, y, length, dx, dy, n - 1, color, s);
    }

    return s;
}

def draw (s : HiScreen) {
    draw_lines(PADDING, PADDING, WIDTH - PADDING * 2, GRID_WIDTH, 0, 9, (0, 0, 0), s);
    draw_lines(PADDING, PADDING, HEIGHT - PADDING * 2, 0, GRID_HEIGHT, 9, (0, 0, 0), s);
    return s;
}

def checkMany(row : Int, col : Int, stepRow : Int, stepCol : Int, playerID : Int) : Bool {
    if (row < 0 or row > 7) { return False; }
    if (col < 0 or col > 7) { return False; }

    if (game.board[row][col] == playerID) {
        return True;
    }
    else {
        if (game.board[row][col] == 3 - playerID) {
            return checkMany(row + stepRow, col + stepCol, stepRow, stepCol, playerID);
        }
        else {
            return False;
        }
    }

    return False;
}

def checkSome(row : Int, col : Int, stepRow : Int, stepCol : Int, playerID : Int) : Bool {
    if (row < 0 or row > 7) { return False; }
    if (col < 0 or col > 7) { return False; }

    if (game.board[row][col] == 3 - playerID) {
        return checkMany(row + stepRow, col + stepCol, stepRow, stepCol, playerID);
    }

    return False;
}

def isValid(row : Int, col : Int, playerID : Int) : Bool {
    if (game.board[row][col] == 1 or game.board[row][col] == 2) {
        return False;
    }

    var flag : Bool = False;
    flag = flag or checkSome(row, col - 1, 0, -1, playerID);
    if (flag) { return True; }
    flag = flag or checkSome(row, col + 1, 0, 1, playerID);
    if (flag) { return True; }
    flag = flag or checkSome(row - 1, col, -1, 0, playerID);
    if (flag) { return True; }
    flag = flag or checkSome(row + 1, col, 1, 0, playerID);
    if (flag) { return True; }
    flag = flag or checkSome(row - 1, col - 1, -1, -1, playerID);
    if (flag) { return True; }
    flag = flag or checkSome(row - 1, col + 1, -1, 1, playerID);
    if (flag) { return True; }
    flag = flag or checkSome(row + 1, col - 1, 1, -1, playerID);
    if (flag) { return True; }
    flag = flag or checkSome(row + 1, col + 1, 1, 1, playerID);

    return flag;
}

def updateBoard(row : Int, col : Int, stepRow : Int, stepCol : Int, playerID) {
    if (game.board[row][col] == 3 - playerID) {
        game.board[row][col] = playerID;
        return updateBoard(row + stepRow, col + stepCol, stepRow, stepCol, playerID);
    }

    return unit;
}

def putDownChess(row : Int, col : Int, playerID : Int) {
    game.board[row][col] = playerID;
    if (checkSome(row, col - 1, 0, -1, playerID)) {
        updateBoard(row, col - 1, 0, -1, playerID);
    }

    if (checkSome(row, col + 1, 0, 1, playerID)) {
        updateBoard(row, col + 1, 0, 1, playerID);
    }

    if (checkSome(row - 1, col, -1, 0, playerID)) {
        updateBoard(row - 1, col, -1, 0, playerID);
    }

    if (checkSome(row + 1, col, 1, 0, playerID)) {
        updateBoard(row + 1, col, 1, 0, playerID);
    }

    if (checkSome(row - 1, col - 1, -1, -1, playerID)) {
        updateBoard(row - 1, col - 1, -1, -1, playerID);
    }

    if (checkSome(row - 1, col + 1, -1, 1, playerID)) {
        updateBoard(row - 1, col + 1, -1, 1, playerID);
    }

    if (checkSome(row + 1, col - 1, 1, -1, playerID)) {
        updateBoard(row + 1, col - 1, 1, -1, playerID);
    }

    if (checkSome(row + 1, col + 1, 1, 1, playerID)) {
        updateBoard(row + 1, col + 1, 1, 1, playerID);
    }
}

def set_next_valid(row : Int, col : Int, playerID : Int) : Unit {
    if (isValid(row, col, playerID)) {
        putDownChess(row, col, playerID);
        return unit;
    }

    if (col < 7) {
        return set_next_valid(row, col + 1, playerID);
    }
    else {
        if (row < 7) {
            return set_next_valid(row + 1, 0, playerID);
        }
    }

    return unit;
}

def checkNextValid(row : Int, col : Int, playerID : Int) : Bool {
    if (isValid(row, col, playerID)) {
        return True;
    }

    if (col < 7) {
        return checkNextValid(row, col + 1, playerID);
    }
    else {
        if (row < 7) {
            return checkNextValid(row + 1, 0, playerID);
        }
    }

    return False;
}

def countChess(playerID : Int, row : Int, col : Int, cnt : Int) : Int {
    if (game.board[row][col] == playerID) {
        cnt = cnt + 1;
    }

    if (col < 7) {
        return countChess(playerID, row, col + 1, cnt);
    }
    else {
        if (row < 7) {
            return countChess(playerID, row + 1, 0, cnt);
        }
        else {
            return cnt;
        }
    }

    return 0;
}

def checkGameResult() : Int {
    var playerOne = checkNextValid(0, 0, 1);
    var playerTwo = checkNextValid(0, 0, 2);
    if (playerOne or playerTwo) {
        return 0;
    }
    else {
        var c1 = countChess(1, 0, 0, 0);
        var c2 = countChess(2, 0, 0, 0);
        if (c1 > c2) { 
            print("Player Wins!");
            return 1; 
        }

        if (c1 < c2) { 
            print("Computer Wins!");
            return 2; 
        }
    }

    return 3;
}

def draw_list_to_chess(lst : List[List[Int]], row : Int, col : Int, s : HiScreen) {
    if (lst[row][col] == 1) {
        drawCircle(PADDING + col * GRID_WIDTH + GRID_WIDTH / 2, 
                    PADDING + row * GRID_HEIGHT + GRID_HEIGHT / 2,
                    RADIUS, BLACK, s);
    }

    if (lst[row][col] == 2) {
        drawCircle(PADDING + col * GRID_WIDTH + GRID_WIDTH / 2, 
                    PADDING + row * GRID_HEIGHT + GRID_HEIGHT / 2,
                    RADIUS, WHITE, s);
    }


    if (col < 7) {
        draw_list_to_chess(lst, row, col + 1, s);
    }
    else {
        if (row < 7) {
            draw_list_to_chess(lst, row + 1, 0, s);
        }
        else {
            return unit;
        }
    }
}

def draw_board(s : HiScreen) {
    draw_list_to_chess(game.board, 0, 0, s);
    return s;
}

def clear(s : HiScreen) {
    s.fill((160, 160, 160));
    return s;
}

def on_mouse_down(s : HiScreen, pos : (Int, Int)) {
    if (game.turn == 2) {
        return unit;
    }

    var x = pos[0];
    var y = pos[1];

    var col = (x - PADDING) / GRID_WIDTH;
    var row = (y - PADDING) / GRID_HEIGHT;

    if (not isValid(row, col, 1)) {
        return unit;
    }

    putDownChess(row, col, 1);

    if (checkGameResult() > 0) {
        return unit;
    }

    var f = clear |> draw |> draw_board |> updateScreen;
    f(s);

    game.turn = 2;

    delaySystem(1000);
    set_next_valid(0, 0, 2);
    if (checkGameResult() > 0) {
        return unit;
    }

    while (not checkNextValid(0, 0, 1)) {
        delaySystem(1000);
        set_next_valid(0, 0, 2);
        f(s);
        if (checkGameResult() > 0) {
            return unit;
        }
    }

    game.turn = 1;
}

s.add_action("MOUSE_DOWN", on_mouse_down);

s.run(clear |> draw |> draw_board);

